<?php
// +----------------------------------------------------------------------
// | yogurt
// +----------------------------------------------------------------------
// +----------------------------------------------------------------------
// | Author: fengyi <1719847255.qq.com>
// +----------------------------------------------------------------------

namespace yogurt;

class Middleware
{
    /**
     * 中间件属性数组
     * @var array $middlewareProperties
     */
    public static $middlewareProperties = [];

    /**
     * 要执行的控制器对象
     * @var $obj
     */
    public static $obj;

    /**
     * 要传入控制器的参数
     * @var $param
     */
    public static $param;

    /**
     * 当前控制器反射类
     * @var null
     */
    public static $refClass = null;

    /**
     * 中间件类
     * @var array $middlewareClass
     */
    public static $middlewareClass = [];

    /**
     * 获取控制器中间件属性
     * @param $obj
     * @param $className
     * @return array
     * @throws Exception
     * @throws \ReflectionException
     */
    public static function getProperties($obj, $className): array
    {
        if (is_null(self::$refClass)) {
            self::$refClass = new \ReflectionClass($className);
        }
        $properties = self::$refClass->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED | \ReflectionProperty::IS_PRIVATE);
        foreach ($properties as $prop) {
            $prop->setAccessible(true);
            if ($prop->getName() == Config::get('middleware.default_properties')) {
                self::$middlewareProperties = array_merge(self::$middlewareProperties, $prop->getValue($obj));
            }
        }
        return self::$middlewareProperties;
    }

    /**
     * 获得中间件类
     * @param $obj
     * @param $className
     * @return array
     * @throws Exception
     * @throws \ReflectionException
     */
    public static function getClass($obj, $className): array
    {
        $classNameArr = self::getProperties($obj, $className);
        foreach ($classNameArr as $value) {
            $classNameFile = str_replace(array('app', '\\'), array(APP_PATH, DIRECTORY_SEPARATOR), $value);
            Yogurt::includeFile($classNameFile . PHP_SUFFIX);
            $obj = new $value();
            array_push(self::$middlewareClass, $obj);
        }
        return self::$middlewareClass;
    }

    /**
     * @param $obj
     * @param $className
     * @param $param
     * @throws \ReflectionException
     * @throws \yogurt\Exception
     */
    public static function init($obj, $className, $param)
    {
        self::$obj = $obj;
        self::$param = $param;
        if (empty(self::$middlewareClass)) {
            self::getClass($obj, $className);
        }
        self::dispatch(new Request);
    }

    /**
     * 调用中间件
     * @param $request
     */
    public static function dispatch($request)
    {
        call_user_func_array(self::resolve(), [$request]);
    }

    /**
     * 中间件匿名函数
     * @return \Closure
     */
    public static function resolve(): \Closure
    {
        return function ($request) {
            $middleware = array_shift(self::$middlewareClass);
            if ($middleware != null) {
                if (!method_exists($middleware, self::$param['handle'])) {
                    throw new Exception("class " . get_class($middleware) . " has not " . self::$param['handle'] . " method.\r\n");
                }
                call_user_func_array([$middleware, self::$param['handle']], [$request, self::resolve()]);
            } else {
                self::start($request);
            }
        }; // 分号不能省略
    }

    /**
     * 框架启动方法
     * @param $request
     * @throws \yogurt\Exception
     */
    public static function start($request)
    {
        // Request注入控制器参数，必须是第一个参数
        // 要解决改限制，可使用反射实现，目前暂不实现
        if (isset(self::$param['args'][0]) && empty(self::$param['args'][0])) {
            self::$param['args'][0] = $request;
        }
        self::execControllerAction(self::$obj, empty(self::$param['action']) ? Config::get('app.default_action') : self::$param['action'], empty(self::$param['args']) ? [new Request] : self::$param['args']);
    }


    /**
     * 控制器前置方法
     * @param $obj
     * @param $action
     */
    public static function execControllerBeforeAction($obj, $action)
    {
        $beforeAction = '__before' . ucwords($action);
        if (method_exists($obj, $beforeAction)) {
            call_user_func_array([$obj, $beforeAction], [new Request()]);
        }
    }

    /**
     * 执行控制器方法
     * @param $obj
     * @param $action
     * @param $args
     */
    public static function execControllerAction($obj, $action, $args)
    {
        // 控制器前置执行
        self::execControllerBeforeAction($obj, $action);
        // 调用控制器方法
        $rest = call_user_func_array([$obj, $action], $args);
        if (is_string($rest)) {
            echo $rest;
        }
        if (is_array($rest)) {
            print_r($rest);
        }
        if (is_object($rest)) {
            var_dump($rest);
        }
        // 控制器后置执行
        self::execControllerAfterAction($obj, $action);
    }

    /**
     * 控制器后置方法
     * @param $obj
     * @param $action
     */
    public static function execControllerAfterAction($obj, $action)
    {
        $beforeAction = '__after' . ucwords($action);
        if (method_exists($obj, $beforeAction)) {
            call_user_func_array([$obj, $beforeAction], [new Request()]);
        }
    }

}
